<?php
/*
邮件发送器
用以生成邮件头，邮件内容，邮件附件
可设置邮件内容是否用HTML格式
可设置某个文件为邮件内容
群发时可设置是否让接收者可见其他的接收者地址
lsj 2009-2-25

#使用方法：
$mail = new Mailer;//声明一实例
$mail->SetError(1);//置为报错状态
$mail->SetDebug(1);//置为调试状态
$mail->SetCharSet('UTF-8');//设置字符编码

$mail->SetSmtp('smtp.163.com', 'union100', 'openmymail8;');//设置SMTP服务器

$mail->AddTo('union100@sina.com', '蓝色');//添加接收人地址方法一,支持中文
$mail->AddTo('picture108@sina.com');//添加接收人地址方法二

$mail->AddCc('union100@126.com');//添加抄送人地址方法一
$mail->AddCc('union100@126.com', '我');//添加抄送人地址方法二

$mail->AddBcc('union100@126.com');//添加密送人地址方法一
$mail->AddBcc('union100@126.com', '我');//添加密送人地址方法二

$mail->AddReplyTo('union100@126.com', 'lsj');//添加回复人地址方法一
$mail->AddReplyTo('union100@126.com');//添加回复人地址方法二

$mail->AddConfirmReadingTo('union100@126.com');//添加确认邮件被读的接收地址,只一个地址

$mail->SetContent('test', '<h3>来啦……</h3>');//可以是HTML格式，也可以是纯文本格式

$mail->AddAttachment('C:\*\2.gif','还好啦');//加附件方法一
$mail->AddAttachment('C:\*\2.gif');//加附件方法二
//路径中不能有中文，好像对中文文件名支持的不好

$result = $mail->Send(true);//发送，true是循环单发，false是

$mail->Close();//关闭SMTP连接


*/
if(!defined('APP_HOME')) exit('Access Denied');

class lib_class_mail
{
	private $Smtp			= null;#SMTP服务器类

	private $To				= null;#接收者地址,可一个或多个,格式：lishujie<union100@sina.com>,*,*
	private $Cc             = null;#抄送者地址,同上
	private $Bcc            = null;#密送者地址,同上
	private $ReplyTo        = null;#回复者地址,同上
	private $From           = null;#发送者地址,同上
	private $ConfirmReadingTo = null;#确认邮件被读的接收地址，同上

	private $Subject		= '';#邮件标题
	private $Body			= '';#邮件体，可以是纯文本也可以是HTML格式的内容
	private $AltBody		= '';#邮件体附加信息，可以省略

	private $MessageType	= '';#邮件信息类型
	private $ContentType    = 'text/plain';#ContentType类型

	private $boundary		= array();#分界
	private $attachment		= array();#附件

	private $Priority       = 3;#Email priority 1：High, 3：Normal, 5：low
	private $CharSet        = 'iso-8859-1';#邮件字符编码
	private $Encoding       = '8bit';#加密方式
	private $WordWrap		= 0;#换行
	private $LN				= "\n";#行结束符

	private $BadAddr		= array();#记录坏的接收地址
	private $IsLogin		= false;#SMTP服务器是否要身份验证
	private $IsError		= 'off';#报错开关
	private $IsDebug		= 'off';#调试开关

	/*
	构造函数
	*/
	function __construct()
	{
		$this->Smtp = new mailerSmtp;
	}

	##########################属性设置与邮件发送#########################

	/*
	添加接收地址
	*/
	function AddTo($addr, $m = '')
	{
		if($m) $addr = $this->EncodeHeader($m, 'phrase').'<'.$addr.'>';
		if($this->To !== null) $this->To .= ','.$addr;
		else $this->To = $addr;
		return $this->To;
	}


	/*
	添加抄送地址
	*/
	function AddCc($addr, $m = '')
	{
		if($m) $addr = $this->EncodeHeader($m, 'phrase').'<'.$addr.'>';
		if($this->Cc !== null) $this->Cc .= ','.$addr;
		else $this->Cc = $addr;
		return $this->Cc;
	}


	/*
	添加密送地址
	*/
	function AddBcc($addr, $m = '')
	{
		if($m) $addr = $this->EncodeHeader($m, 'phrase').'<'.$addr.'>';
		if($this->Bcc !== null) $this->Bcc .= ','.$addr;
		else $this->Bcc = $addr;
		return $this->Bcc;
	}


	/*
	添加回复地址
	*/
	function AddReplyTo($addr, $m = '')
	{
		if($m) $addr = $this->EncodeHeader($m, 'phrase').'<'.$addr.'>';
		if($this->ReplyTo !== null) $this->ReplyTo .= ','.$addr;
		else $this->ReplyTo = $addr;
		return $this->ReplyTo;
	}

	/*
	清空接收地址
	*/
	function ClearTo()
	{
		$this->To = null;
	}

	/*
	清空抄送地址
	*/
	function ClearCc()
	{
		$this->Cc = null;
	}

	/*
	清空密送地址
	*/
	function ClearBcc()
	{
		$this->Bcc = null;
	}

	/*
	清空回复地址
	*/
	function ClearReplyTo()
	{
		$this->ReplyTo = null;
	}

	/*
	添加邮件被读时发送的确认地址，只能加一个地址
	*/
	function AddConfirmReadingTo($addr)
	{
		$this->ConfirmReadingTo = trim($addr);
		return $this->ConfirmReadingTo;
	}


	/*
	设置SMTP服务器
	*/
	function SetSmtp($host, $username = '', $password = '', $from = '', $fromname = '', $port = 25, $timeout = 10)
	{
		if($from) $this->From = $from;
		elseif($username) $this->From = $username.str_replace('smtp.', '@', strtolower($host));
		if($fromname) $this->From = $this->EncodeHeader($fromname, 'phrase').'<'.$this->From.'>';
		if($username && $password) $this->IsLogin = true;
		return $this->Smtp->SetSmtp($host, $username, $password, $port, $timeout);
	}


	/*
	设置邮件的标题和内容，内容可为空
	*/
	function SetContent($subject, $content = '')
	{
		$this->Subject = $subject;
		$this->Body = $content;
		return true;
	}


	/*
	设置邮件的附加信息
	*/
	function SetText($text)
	{
		$this->AltBody = $text;
		return true;
	}


	/*
	设置字符编码
	*/
	function SetCharSet($char)
	{
		$this->CharSet = $char;
		return true;
	}


	/*
	设置换行长度
	*/
	function SetWordWrap($n)
	{
		$this->WordWrap = (int)$n;
		return true;
	}


	/*
	设置邮件编码
	支持以下几种："8bit", "7bit", "binary", "base64", and "quoted-printable"
	*/
	function SetEncoding($code)
	{
		$this->Encoding = $code;
		return true;
	}


	/*
	设置报错开关
	*/
	function SetError($is)
	{
		if(strtolower($is) == 'on' || (int)$is == 1 || $is === true) $this->IsError = 'on';
		else $this->IsError = 'off';
		$this->Smtp->SetError($is);
		return true;
	}


	/*
	设置调试开关
	*/
	function SetDebug($is)
	{
		if(strtolower($is) == 'on' || (int)$is == 1 || $is === true) $this->IsDebug = 'on';
		else $this->IsDebug = 'off';
		$this->Smtp->SetDebug($is);
		return true;
	}


	/*
	发送邮件
	参数IsOnly表示是否单封循环发送
	*/
	function Send($IsOnly = false)
	{
		$this->BadAddr['rcpt'] = array();
		$this->BadAddr['data'] = array();

		#连接SMTP服务器
		if(!$this->Smtp->Connect()) return false;

		#生成ContentType
		if(!empty($this->AltBody)) $this->ContentType = 'multipart/alternative';
		$this->SetMessageType();

		#
		if($IsOnly === true){#循环单封发送
			$theto = $this->To;
			$thecc = $this->Cc;
			$thebcc = $this->Bcc;
			$this->Cc = null;
			$this->Bcc = null;
			$onlyto = explode(',', $this->To);
			$this->To = null;
			if($onlyto){
				foreach($onlyto as $val){
					if(empty($val)) continue;
					$this->To = $val;
					#身份验证并发送HELLO,MAIL指令
					if(!$this->SendFirst()) continue;
					#标明接受地址，当有坏地址时自动放弃它
					preg_match('/<([^>]*)>/i',$val, $vv);
					if(empty($vv)) $vv = $val;
					else $vv = $vv[1];
					if(!$this->Smtp->Rcpt($vv)){
						$this->Smtp->Reset();
						$this->BadAddr['rcpt'][] = $this->To;
						continue;
					}
					#准备邮件的头和内容
					$header	= $this->CreateHeader();
					$body	= $this->CreateBody();
					if(!$header || !$body) continue;
					if($this->IsDebug == 'on') echo "\n***EMAIL HEAD AND BODY***\n".$header.$body."******\n";
					#发送数据
					if(!$this->Smtp->Data($header.$body)) $this->BadAddr['data'][] = $val;
					$this->Smtp->Quit();
					$this->Smtp->Reset();
				}
			}
			if($thecc !== null || $thebcc !== null){#当有抄送或密送时
				#身份验证并发送HELLO,MAIL指令
				if(!$this->SendFirst()) return false;
				#标明接受地址，当有坏地址时自动放弃它
				$this->To = null;
				$this->Cc = $thecc;
				$this->Bcc = $thebcc;
				$this->RcptAddr('cc', $this->Cc);
				$this->RcptAddr('bcc', $this->Bcc);
				#准备邮件的头和内容
				$header	= $this->CreateHeader();
				$body	= $this->CreateBody();
				if(!$header || !$body) return false;
				if($this->IsDebug == 'on') echo "\n***EMAIL HEAD AND BODY***\n".$header.$body."******\n";
				#发送
				$this->Smtp->Data($header.$body);
				$this->Smtp->Reset();
			}
			$this->To = $theto;
		}else{#一次性多发
			#身份验证并发送HELLO,MAIL指令
			if(!$this->SendFirst()) return false;
			#标明接受地址，当有坏地址时自动放弃它
			$this->RcptAddr('to', $this->To);
			$this->RcptAddr('cc', $this->Cc);
			$this->RcptAddr('bcc', $this->Bcc);
			#准备邮件的头和内容
			$header	= $this->CreateHeader();
			$body	= $this->CreateBody();
			if(!$header || !$body) continue;
			if($this->IsDebug == 'on') echo "\n***EMAIL HEAD AND BODY***\n".$header.$body."******\n";
			#发送
			$this->Smtp->Data($header.$body);
			$this->Smtp->Quit();
			$this->Smtp->Reset();
		}

		#返回成功与失败的记录
		$to_cnt = $this->To === null ? 0 : substr_count($this->To, ',') + 1;
		$cc_cnt = $this->Cc === null ? 0 : substr_count($this->Cc, ',') + 1;
		$bcc_cnt = $this->Bcc === null ? 0 : substr_count($this->Bcc, ',') + 1;
		$rcpt_cnt = count($this->BadAddr['rcpt']);
		$data_cnt = count($this->BadAddr['data']);
		return array($to_cnt, $cc_cnt, $bcc_cnt, $rcpt_cnt, $data_cnt);
	}


	/*
	获得坏的地址
	*/
	function GetBadAddr()
	{
		return $this->BadAddr;
	}


	/*
	关闭SMTP连接
	*/
	function Close()
	{
		$this->Smtp->Quit();
		$this->Smtp->Close();
	}


	/*
	设置邮件信息的类型
	*/
	function SetMessageType()
	{
		if(count($this->attachment) < 1 && strlen($this->AltBody) < 1) {
			$this->MessageType = 'plain';
		} else {
			if(count($this->attachment) > 0) {
				$this->MessageType = 'attachments';
			}
			if(strlen($this->AltBody) > 0 && count($this->attachment) < 1) {
				$this->MessageType = 'alt';
			}
			if(strlen($this->AltBody) > 0 && count($this->attachment) > 0) {
				$this->MessageType = 'alt_attachments';
			}
		}
	}


	/*
	预发送邮件数据前发送几个指令
	*/
	function SendFirst(){
		#发送hello指令
		$hello = 'localhost.localdomain';
		if(isset($_SERVER['SERVER_NAME'])) $hello = $_SERVER['SERVER_NAME'];
		if(!$this->Smtp->Hello($hello)) return false;

		#身份验证
		if($this->IsLogin === true && !$this->Smtp->Login()){
			$this->Smtp->Reset();
			return false;
		}

		#发送MAIL指令
		if(!$this->Smtp->Mail($this->From)){
			$this->Smtp->Reset();
			return false;
		}

		return true;
	}


	/*
	#标明接受地址
	*/
	function RcptAddr($type, $addr)
	{
		if(!$addr) return true;
		$theAddr = null;
		$type = strtolower($type);
		$toAttr = explode(',', $addr);
		foreach($toAttr as $v){
			if(!empty($v)){
				preg_match('/<([^>]*)>/i',$v, $vv);
				if(empty($vv)) $vv = $v;
				else $vv = $vv[1];
				if(!$this->Smtp->Rcpt($vv)){
					$this->BadAddr['rcpt'][] = $v;
				}else{
					if($theAddr === null) $theAddr = $v;
					else $theAddr .= ','.$v;
				}
			}
		}
		if($type == 'to') $this->To = $theAddr;
		elseif($type == 'cc') $this->Cc = $theAddr;
		elseif($type == 'bcc') $this->Bcc = $theAddr;
		else return false;
		return true;
	}

	###################创建邮件头与邮件内容函数###################

	/*
	创建邮件头
	*/
	function CreateHeader()
	{
		$result = '';
		if($this->From === null || empty($this->From)){
			$this->GetError(5);#发送地址不能为空
			return false;
		}
		if($this->To === null && $this->Cc === null && $this->Bcc === null){
			$this->GetError(6);#接收地址不能为空
			return false;
		}

		$uniq_id = md5(uniqid(time()));
		$this->boundary[1] = 'b1_' . $uniq_id;
		$this->boundary[2] = 'b2_' . $uniq_id;

		//检查是否有HTML标签
		if(eregi('<[^>]+>.*</[^>]+>', $this->Body)) $this->ContentType = 'text/html';

		$result .= $this->HeaderLine('Date', $this->RFCDate());
		$result .= $this->HeaderLine('Return-Path', trim($this->From));

		if($this->To !== null) {
			$result .= $this->HeaderLine('To', $this->To);
		} elseif ($this->Cc == null) {
			$result .= $this->HeaderLine('To', 'undisclosed-recipients:;');
		}

		if($this->Cc !== null) $result .= $this->HeaderLine('Cc', $this->Cc);
		$result .= $this->HeaderLine('From', $this->From);
		if($this->Bcc !== null) $result .= $this->HeaderLine('Bcc', $this->Bcc);
		if($this->ReplyTo !== null) $result .= $this->HeaderLine('Reply-to', $this->ReplyTo);

		$subject = str_replace(array("\r", "\n"), '', $this->Subject);
		$result .= $this->HeaderLine('Subject', $this->EncodeHeader($subject));
		$result .= sprintf("Message-ID: <%s.%s>%s", $uniq_id, $this->From, $this->LN);
		$result .= $this->HeaderLine('X-Priority', $this->Priority);
		$result .= $this->HeaderLine('X-Mailer', 'omo99_mailer');

		if($this->ConfirmReadingTo !== null) $result .= $this->HeaderLine('Disposition-Notification-To', '<'.trim($this->ConfirmReadingTo).'>');

		$result .= $this->HeaderLine('MIME-Version', '1.0');

		switch($this->MessageType) {
			case 'plain':
				$result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding);
				$result .= sprintf("Content-Type: %s; charset=\"%s\"", $this->ContentType, $this->CharSet);
				break;
			case 'attachments':
			case 'alt_attachments':
				if($this->InlineImageExists()){
					$result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", 'multipart/related', $this->LN, $this->LN, $this->boundary[1], $this->LN);
				} else {
					$result .= $this->HeaderLine('Content-Type', 'multipart/mixed;');
					$result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
				}
				break;
			case 'alt':
				$result .= $this->HeaderLine('Content-Type', 'multipart/alternative;');
				$result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
				break;
		}

		$result .= $this->LN.$this->LN;

		return $result;
	}


	/*
	创建邮件体
	*/
	function CreateBody()
	{
		$result = '';
		$this->DoWordWrap();

		switch($this->MessageType) {
			case 'alt':
				$result .= $this->GetBoundary($this->boundary[1], '', 'text/plain', '');
				$result .= $this->EncodeString($this->AltBody, $this->Encoding);
				$result .= $this->LN.$this->LN;
				$result .= $this->GetBoundary($this->boundary[1], '', 'text/html', '');
				$result .= $this->EncodeString($this->Body, $this->Encoding);
				$result .= $this->LN.$this->LN;
				$result .= $this->EndBoundary($this->boundary[1]);
				break;
			case 'plain':
				$result .= $this->EncodeString($this->Body, $this->Encoding);
				break;
			case 'attachments':
				$result .= $this->GetBoundary($this->boundary[1], '', '', '');
				$result .= $this->EncodeString($this->Body, $this->Encoding);
				$result .= $this->LN;
				$result .= $this->AttachAll();
				break;
			case 'alt_attachments':
				$result .= sprintf("--%s%s", $this->boundary[1], $this->LN);
				$result .= sprintf("Content-Type: %s;%s" . "\tboundary=\"%s\"%s", 'multipart/alternative', $this->LN, $this->boundary[2], $this->LN.$this->LN);
				$result .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '') . $this->LN;
				$result .= $this->EncodeString($this->AltBody, $this->Encoding);
				$result .= $this->LN.$this->LN;
				$result .= $this->GetBoundary($this->boundary[2], '', 'text/html', '') . $this->LN;
				$result .= $this->EncodeString($this->Body, $this->Encoding);
				$result .= $this->LN.$this->LN;
				$result .= $this->EndBoundary($this->boundary[2]);
				$result .= $this->AttachAll();
				break;
		}
		return $result;
	}


	/*
	获取边界
	*/
	function GetBoundary($boundary, $charSet, $contentType, $encoding)
	{
		$result = '';
		if($charSet == '') {
			$charSet = $this->CharSet;
		}
		if($contentType == '') {
			$contentType = $this->ContentType;
		}
		if($encoding == '') {
			$encoding = $this->Encoding;
		}
		$result .= $this->TextLine('--' . $boundary);
		$result .= sprintf("Content-Type: %s; charset = \"%s\"", $contentType, $charSet);
		$result .= $this->LN;
		$result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding);
		$result .= $this->LN;
		return $result;
	}


	/*
	获取边界的结束
	*/
	function EndBoundary($boundary)
	{
		return $this->LN . '--' . $boundary . '--' . $this->LN;
	}


	/*
	使内容Wrapping
	*/
	function WrapText($message, $length, $qp_mode = false)
	{
		$soft_break = ($qp_mode) ? sprintf(" =%s", $this->LN) : $this->LN;

		$message = str_replace(array("\r\n", "\n"), $this->LN, trim($message));
		if (substr($message, -1) == $this->LN) {
			$message = substr($message, 0, -1);
		}

		$line = explode($this->LN, $message);
		$message = '';
		$counts = count($line);
		for ($i=0 ; $i < $counts; $i++) {
			$line_part = explode(' ', $line[$i]);
			$buf = '';
			$cnts = count($line_part);
			for ($e = 0; $e < $cnts; $e++) {
				$word = $line_part[$e];
				if ($qp_mode and (strlen($word) > $length)) {
					$space_left = $length - strlen($buf) - 1;
					if ($e != 0) {
						if ($space_left > 20) {
							$len = $space_left;
							if (substr($word, $len - 1, 1) == '=') {
								$len--;
							} elseif (substr($word, $len - 2, 1) == '=') {
								$len -= 2;
							}
							$part = substr($word, 0, $len);
							$word = substr($word, $len);
							$buf .= ' ' . $part;
							$message .= $buf . sprintf("=%s", $this->LN);
						} else {
							$message .= $buf . $soft_break;
						}
						$buf = '';
					}
					while (strlen($word) > 0) {
						$len = $length;
						if (substr($word, $len - 1, 1) == '=') {
							$len--;
						} elseif (substr($word, $len - 2, 1) == '=') {
							$len -= 2;
						}
						$part = substr($word, 0, $len);
						$word = substr($word, $len);

						if (strlen($word) > 0) {
							$message .= $part . sprintf("=%s", $this->LN);
						} else {
							$buf = $part;
						}
					}
				} else {
					$buf_o = $buf;
					$buf .= ($e == 0) ? $word : (' ' . $word);

					if (strlen($buf) > $length && $buf_o != '') {
						$message .= $buf_o . $soft_break;
						$buf = $word;
					}
				}
			}
			$message .= $buf . $this->LN;
		}
		return $message;
	}


	/*
	设置邮件体的WordWrap
	*/
	function DoWordWrap()
	{
		if($this->WordWrap < 1) {
			return;
		}
		switch($this->MessageType) {
			case 'alt':
			case 'alt_attachments':
				$this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap);
				break;
			default:
				$this->Body = $this->WrapText($this->Body, $this->WordWrap);
				break;
		}
	}


	/*
	生成邮件头的各行内容
	*/
	function HeaderLine($name, $value)
	{
		return $name . ': ' . $value . $this->LN;
	}


	/*
	生成邮件体的各行内容
	*/
	function TextLine($value)
	{
		return $value . $this->LN;
	}

	########################附件函数,一般用前四个########################

	/*
	添加普通格式的附件
	*/
	function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream')
	{
		if(!@is_file($path)) {
			$this->GetError(1);#
			return false;
		}

		$filename = basename($path);
		if($name == '') $name = $filename;

		$cur = count($this->attachment);
		$this->attachment[$cur][0] = $path;
		$this->attachment[$cur][1] = $filename;
		$this->attachment[$cur][2] = $name;
		$this->attachment[$cur][3] = $encoding;
		$this->attachment[$cur][4] = $type;
		$this->attachment[$cur][5] = false;
		$this->attachment[$cur][6] = 'attachment';
		$this->attachment[$cur][7] = 0;
		return true;
	}


	/*
	添加字符串或二进制内容做为附件
	*/
	function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream')
	{
		$cur = count($this->attachment);
		$this->attachment[$cur][0] = $string;
		$this->attachment[$cur][1] = $filename;
		$this->attachment[$cur][2] = $filename;
		$this->attachment[$cur][3] = $encoding;
		$this->attachment[$cur][4] = $type;
		$this->attachment[$cur][5] = true;
		$this->attachment[$cur][6] = 'attachment';
		$this->attachment[$cur][7] = 0;
	}


	/*
	格式化HTML格式的内容
	*/
	function MsgHTML($message)
	{
		preg_match_all("/(src|background)=\"(.*)\"/Ui", $message, $images);
		if(isset($images[2])){
			foreach($images[2] as $i => $url){
				$filename  = basename($url);
				$directory = dirname($url);
				$cid       = 'cid:' . md5($filename);
				$fileParts = split("\.", $filename);
				$ext       = $fileParts[1];
				$mimeType  = $this->GetMimeType($ext);
				$message = preg_replace("/".$images[1][$i]."=\"".preg_quote($url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $message);
				$this->AddEmbeddedImage($url, md5($filename), $filename, 'base64', $mimeType);
			}
		}

		$this->ContentType = 'text/html';
		$this->Body = $message;
		$textMsg = trim(strip_tags($message));
		if(!empty($textMsg) && empty($this->AltBody)) {
			$this->AltBody = $textMsg;
		}
		if (empty($this->AltBody)){
			$this->AltBody = '请用HTML模式查看邮件' . "\n\n";
		}
	}


	/*
	取文件的内容
	*/
	function GetFile($filename)
	{
		$return = '';
		if($fp = fopen($filename, 'rb')){
			while (!feof($fp)){
				$return .= fread($fp, 1024);
			}
			fclose($fp);
			return $return;
		}else{
			return false;
		}
	}


	/*
	生成所有的格式的附件
	*/
	function AttachAll()
	{
		$mime = array();
		$cnt = count($this->attachment);
		for($i = 0; $i < $cnt; $i++) {
			$bString = $this->attachment[$i][5];
			if ($bString) {
				$string = $this->attachment[$i][0];
			} else {
				$path = $this->attachment[$i][0];
			}

			$filename    = $this->attachment[$i][1];
			$name        = $this->attachment[$i][2];
			$encoding    = $this->attachment[$i][3];
			$type        = $this->attachment[$i][4];
			$disposition = $this->attachment[$i][6];
			$cid         = $this->attachment[$i][7];

			$mime[] = sprintf("--%s%s", $this->boundary[1], $this->LN);
			$mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $name, $this->LN);
			$mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LN);

			if($disposition == 'inline') {
				$mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LN);
			}

			$mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $name, $this->LN.$this->LN);

			if($bString) {
				$mime[] = $this->EncodeString($string, $encoding);
				$mime[] = $this->LN.$this->LN;
			} else {
				$mime[] = $this->EncodeFile($path, $encoding);
				$mime[] = $this->LN.$this->LN;
			}
		}
		$mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LN);
		return join('', $mime);
	}


	/*
	添加流媒体格式的附件
	*/
	function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream')
	{

		if(!@is_file($path)) {
			$this->GetError(2);#
			return false;
		}

		$filename = basename($path);
		if($name == '') $name = $filename;

		$cur = count($this->attachment);
		$this->attachment[$cur][0] = $path;
		$this->attachment[$cur][1] = $filename;
		$this->attachment[$cur][2] = $name;
		$this->attachment[$cur][3] = $encoding;
		$this->attachment[$cur][4] = $type;
		$this->attachment[$cur][5] = false;
		$this->attachment[$cur][6] = 'inline';
		$this->attachment[$cur][7] = $cid;

		return true;
	}


	/*
	检查当前的附件中有没有流媒体文件
	*/
	function InlineImageExists()
	{
		$result = false;
		for($i = 0; $i < count($this->attachment); $i++) {
			if($this->attachment[$i][6] == 'inline') {
				$result = true;
				break;
			}
		}
		return $result;
	}


	/*
	base64编码加密文件
	*/
	function EncodeFile ($path, $encoding = 'base64')
	{
		if(!@$fd = fopen($path, 'rb')) {
			$this->GetError(3);#
			return false;
		}
		$magic_quotes = get_magic_quotes_runtime();
		set_magic_quotes_runtime(0);
		$file_buffer = fread($fd, filesize($path));
		$file_buffer = $this->EncodeString($file_buffer, $encoding);
		fclose($fd);
		set_magic_quotes_runtime($magic_quotes);

		return $file_buffer;
	}

	/*
	按不同方式加密字符串
	*/
	function EncodeString ($str, $encoding = 'base64')
	{
		$encoded = '';
		switch(strtolower($encoding)) {
			case 'base64':
				$encoded = chunk_split(base64_encode($str), 76, $this->LN);
				break;
			case '7bit':
			case '8bit':
				$encoded = str_replace(array("\r\n", "\n"), $this->LN, trim($str));
				if (substr($encoded, -(strlen($this->LN))) != $this->LN)
				  $encoded .= $this->LN;
				break;
			case 'binary':
				$encoded = $str;
				break;
			case 'quoted-printable':
				$encoded = $this->EncodeQP($str);
				break;
			default:
				$this->GetError(4);#
				break;
		}
		return $encoded;
	}


	/*
	邮件头内的内容编码处理
	*/
	function EncodeHeader ($str, $position = 'text')
	{
		$x = 0;
		switch (strtolower($position)) {
			case 'phrase':
				if (!preg_match('/[\200-\377]/', $str)) {
					$encoded = addcslashes($str, "\0..\37\177\\\"");
					if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
						return ($encoded);
					} else {
						return ("\"$encoded\"");
					}
				}
				$x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
				break;
			case 'comment':
				$x = preg_match_all('/[()"]/', $str, $matches);
			case 'text':
			default:
				$x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
				break;
		}

		if ($x == 0) {
			return ($str);
		}
		$maxlen = 75 - 7 - strlen($this->CharSet);
		if (strlen($str)/3 < $x) {
			$encoding = 'B';
			$encoded = base64_encode($str);
			$maxlen -= $maxlen % 4;
			$encoded = trim(chunk_split($encoded, $maxlen, "\n"));
		} else {
			$encoding = 'Q';
			$encoded = $this->EncodeQ($str, $position);
			$encoded = $this->WrapText($encoded, $maxlen, true);
			$encoded = str_replace('='.$this->LN, "\n", trim($encoded));
		}

		$encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded);
		$encoded = trim(str_replace("\n", $this->LN, $encoded));

		return $encoded;
	}


	/*
	quoted-printable编码加密字符串
	*/
	public function EncodeQP($string, $line_max = 74)
	{
		$fp = fopen('php://temp/', 'r+');
		$string = preg_replace('/\r\n?/', $this->LN, $string);
		$params = array('line-length' => $line_max, 'line-break-chars' => $this->LN);
		stream_filter_append($fp, 'convert.quoted-printable-encode', STREAM_FILTER_READ, $params);
		fputs($fp, $string);
		rewind($fp);
		$out = stream_get_contents($fp);
		$out = preg_replace('/^\./m', '=2E', $out);
		fclose($fp);
		return $out;
	}


	/*
	q编码加密字符串
	*/
	function EncodeQ ($str, $position = 'text')
	{
		$encoded = preg_replace("[\r\n]", '', $str);
		switch (strtolower($position)) {
			case 'phrase':
			$encoded = preg_replace("/([^A-Za-z0-9!*+\/ -])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
			break;
			case 'comment':
			$encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
			case 'text':
			default:
			$encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e',
				  "'='.sprintf('%02X', ord('\\1'))", $encoded);
			break;
		}
		$encoded = str_replace(' ', '_', $encoded);
		return $encoded;
	}


	/*
	匹配MIME类型
	*/
	function GetMimeType($ext = '')
	{
		$mimes = array(
			'hqx'  =>  'application/mac-binhex40',
			'cpt'   =>  'application/mac-compactpro',
			'doc'   =>  'application/msword',
			'bin'   =>  'application/macbinary',
			'dms'   =>  'application/octet-stream',
			'lha'   =>  'application/octet-stream',
			'lzh'   =>  'application/octet-stream',
			'exe'   =>  'application/octet-stream',
			'class' =>  'application/octet-stream',
			'psd'   =>  'application/octet-stream',
			'so'    =>  'application/octet-stream',
			'sea'   =>  'application/octet-stream',
			'dll'   =>  'application/octet-stream',
			'oda'   =>  'application/oda',
			'pdf'   =>  'application/pdf',
			'ai'    =>  'application/postscript',
			'eps'   =>  'application/postscript',
			'ps'    =>  'application/postscript',
			'smi'   =>  'application/smil',
			'smil'  =>  'application/smil',
			'mif'   =>  'application/vnd.mif',
			'xls'   =>  'application/vnd.ms-excel',
			'ppt'   =>  'application/vnd.ms-powerpoint',
			'wbxml' =>  'application/vnd.wap.wbxml',
			'wmlc'  =>  'application/vnd.wap.wmlc',
			'dcr'   =>  'application/x-director',
			'dir'   =>  'application/x-director',
			'dxr'   =>  'application/x-director',
			'dvi'   =>  'application/x-dvi',
			'gtar'  =>  'application/x-gtar',
			'php'   =>  'application/x-httpd-php',
			'php4'  =>  'application/x-httpd-php',
			'php3'  =>  'application/x-httpd-php',
			'phtml' =>  'application/x-httpd-php',
			'phps'  =>  'application/x-httpd-php-source',
			'js'    =>  'application/x-javascript',
			'swf'   =>  'application/x-shockwave-flash',
			'sit'   =>  'application/x-stuffit',
			'tar'   =>  'application/x-tar',
			'tgz'   =>  'application/x-tar',
			'xhtml' =>  'application/xhtml+xml',
			'xht'   =>  'application/xhtml+xml',
			'zip'   =>  'application/zip',
			'mid'   =>  'audio/midi',
			'midi'  =>  'audio/midi',
			'mpga'  =>  'audio/mpeg',
			'mp2'   =>  'audio/mpeg',
			'mp3'   =>  'audio/mpeg',
			'aif'   =>  'audio/x-aiff',
			'aiff'  =>  'audio/x-aiff',
			'aifc'  =>  'audio/x-aiff',
			'ram'   =>  'audio/x-pn-realaudio',
			'rm'    =>  'audio/x-pn-realaudio',
			'rpm'   =>  'audio/x-pn-realaudio-plugin',
			'ra'    =>  'audio/x-realaudio',
			'rv'    =>  'video/vnd.rn-realvideo',
			'wav'   =>  'audio/x-wav',
			'bmp'   =>  'image/bmp',
			'gif'   =>  'image/gif',
			'jpeg'  =>  'image/jpeg',
			'jpg'   =>  'image/jpeg',
			'jpe'   =>  'image/jpeg',
			'png'   =>  'image/png',
			'tiff'  =>  'image/tiff',
			'tif'   =>  'image/tiff',
			'css'   =>  'text/css',
			'html'  =>  'text/html',
			'htm'   =>  'text/html',
			'shtml' =>  'text/html',
			'txt'   =>  'text/plain',
			'text'  =>  'text/plain',
			'log'   =>  'text/plain',
			'rtx'   =>  'text/richtext',
			'rtf'   =>  'text/rtf',
			'xml'   =>  'text/xml',
			'xsl'   =>  'text/xml',
			'mpeg'  =>  'video/mpeg',
			'mpg'   =>  'video/mpeg',
			'mpe'   =>  'video/mpeg',
			'qt'    =>  'video/quicktime',
			'mov'   =>  'video/quicktime',
			'avi'   =>  'video/x-msvideo',
			'movie' =>  'video/x-sgi-movie',
			'doc'   =>  'application/msword',
			'word'  =>  'application/msword',
			'xl'    =>  'application/excel',
			'eml'   =>  'message/rfc822'
		);
		return (!isset($mimes[strtolower($ext)])) ? 'application/x-unknown-content-type' : $mimes[strtolower($ext)];
	}


	/*
	由时间戳生成随机的唯一的ID号
	*/
	private static function RFCDate() {
		$tz = date('Z');
		$tzs = ($tz < 0) ? '-' : '+';
		$tz = abs($tz);
		$tz = (int)($tz / 3600) * 100 + ($tz % 3600) / 60;
		$result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz);

		return $result;
	}


	/*
	报错处理
	*/
	private function GetError($errno = 0)
	{
		#错误号与错误信息
		$errorArr[1] = '要做为普通附件的文件不存在';
		$errorArr[2] = '要做为流媒体的附件的文件不存在';
		$errorArr[3] = '要BASE64加密的文件不存在';
		$errorArr[4] = '没有声明字符串加密格式';

		$errno = (int)$errno;
		if($errno) $this->errno = $errno;

		if(!empty($errorArr[$this->errno])){
			if($this->isError == 'on'){#打印并返回错误信息
				echo '<font color="red">ERROR:'.$errorArr[$this->errno].'</font>';
				return $errorArr[$this->errno];
			}else{#返回错误号和错误信息
				//echo '<!--MAILER ERRNO:'.$this->errno.'-->';
				return array($this->errno, $errorArr[$this->errno]);
			}
		}
		return false;
	}



}



#########################################################
#########################################################

/*
连接SMTP服务器
如须身份验证，可设置用户名和密码，进行登陆
*/

class mailerSmtp
{
	private $lk			= null;#SMTP服务器连接句柄
	private $host		= null;#SMTP服务器
	private $port		= 25;#SMTP服务器端口
	private $timeout	= 30;#连接SMTP服务器的超时时间
	private $username	= null;#登陆SMTP服务器的账户
	private $password	= null;#账户对应的密码

	private $LN			= "\r\n";#SMTP命令行的结束符
	private $errno		= 0;#错误号,对应着错误信息
	private $isError	= 'off';#是否报告错误，关闭时只报告错误号
	private $isDebug	= 'off';#调试开关，值：on,off,1,0


	function __construct()
	{

	}


	/*
	设置SMTP服务器一些参数
	*/
	function SetSmtp($host, $username = '', $password = '', $port = 25, $timeout = 30)
	{
		$this->host = $host;
		if($username) $this->username = $username;
		if($password) $this->password = $password;
		if($port) $this->port = $port;
		if($timeout) $this->timeout = $timeout;
		return true;
	}


	/*
	连接SMTP服务器
	*/
	function Connect()
	{
		if($this->lk) return $this->lk;
		if(!$this->host || !$this->port || !$this->timeout){
			$this->GetError(1);#连接参数不全
			return false;
		}
		$this->lk = fsockopen($this->host, $this->port, $errno, $error, $this->timeout);
		if(!$this->lk) return false;
		if(substr(PHP_OS, 0, 3) != "WIN") socket_set_timeout($this->lk, $this->timeout, 0);
		$this->GetReply();
		return $this->lk;
	}


	/*
	登陆SMTP服务器
	*/
	function Login()
	{
		if($this->username === null || $this->password === null){
			$this->GetError(19);#缺少登陆服务器用户名或密码
			return false;
		}
		#1.开始身份验证
		$this->PutCmd('AUTH LOGIN');
		$reply = $this->GetReply();
		$code = substr($reply, 0, 3);
		if($code != 334){
			$this->GetError(2);#服务器没有接受身份验证机制
			return false;
		}

		#2.验证用户名
		$this->PutCmd(base64_encode($this->username));
		$reply = $this->GetReply();
		$code = substr($reply, 0, 3);
		if($code != 334){
			$this->GetError(3);#服务器没有接受用户名
			return false;
		}

		#3.验证用户密码
		$this->PutCmd(base64_encode($this->password));
		$reply = $this->GetReply();
		$code = substr($reply, 0, 3);
		if($code != 235){
			$this->GetError(4);#服务器没有接受用户密码
			return false;
		}
		return true;
	}


	/*
	关闭SMTP服务器连接
	*/
	function Close()
	{
		if($this->lk){
			fclose($this->lk);
			$this->lk = null;
			return true;
		}
		return false;
	}

	######################SMTP命令函数######################

	/*
	发送DATA命令
	*/
	function Data($data)
	{
		$this->PutCmd('DATA');
		$reply = $this->GetReply();
		$code = substr($reply, 0, 3);
		if($code != 354){
			$this->GetError(17);#服务器没有接受DATA
			return false;
		}

		$data = str_replace("\r\n", "\n", $data);
		$data = str_replace("\r", "\n", $data);
		$lines = explode("\n", $data);

		$field = substr($lines[0], 0, strpos($lines[0],':'));
		$in_headers = false;
		if(!empty($field) && !strstr($field, ' ')) $in_headers = true;

		$max_line_length = 998;

		while(list(, $line) = @each($lines)) {
			$lines_out = null;
			if($line == "" && $in_headers) {
				$in_headers = false;
			}
			while(strlen($line) > $max_line_length) {
				$pos = strrpos(substr($line, 0, $max_line_length), ' ');
				if(!$pos) $pos = $max_line_length - 1;
				$lines_out[] = substr($line, 0, $pos);
				$line = substr($line,$pos + 1);
				if($in_headers) $line = "\t".$line;
			}
			$lines_out[] = $line;
			while(list(,$line_out) = @each($lines_out)) {
				if(strlen($line_out) > 0) {
					if(substr($line_out, 0, 1) == '.') {
						$line_out = '.'.$line_out;
					}
				}
				$this->PutCmd($line_out);
			}
		}

		$this->PutCmd($this->LN.'.');
		$reply = $this->GetReply();
		$code = substr($reply, 0, 3);
		if($code != 250){
			$this->GetError(18);
			return false;
		}
		return true;
	}


	/*
	发送EXPAND命令
	*/
	function Expand($name)
	{
		$this->PutCmd('EXPN '.$name);
		$reply = $this->GetReply();
		$code = substr($reply, 0, 3);
		if($code != 250){
			$this->GetError(5);#服务器没有接受EXPN
			return false;
		}

		$entries = explode($this->LN, $reply);
		while(list(,$l) = @each($entries)) {
		  $list[] = substr($l,4);
		}
		return $list;
	}


	/*
	发送HELLO命令
	*/
	function Hello($hostname = 'localhost.localdomain')
	{
		$this->PutCmd('EHLO '.$hostname);
		$reply = $this->GetReply();
		$code = substr($reply, 0, 3);
		if($code != 250){
			$this->GetError(6);#服务器没有接受EHLO
			$this->PutCmd($this->lk, 'HELO '.$hostname);
			$reply = $this->GetReply();
			$code = substr($reply, 0, 3);
			if($code != 250){
				$this->GetError(7);#服务器没有接受HELO
				return false;
			}
		}
		return true;
	}

	/*
	发送HELP命令
	*/
	function Help($keyword = '')
	{
		if(!empty($keyword)) $keyword = ' '.$keyword;
		$this->PutCmd('HELP '.$keyword);
		$reply = $this->GetReply();
		$code = substr($reply, 0, 3);
		if($code != 211 && $code != 214){
			$this->GetError(8);#服务器没有接受HELP
			return false;
		}
		return true;
	}

	/*
	发送MAIL命令
	*/
	function Mail($from, $verp = false)
	{
		$use_verp = $verp ? 'XVERP' : '';
		$this->PutCmd('MAIL FROM:<'.$from.'>'.$user_verp);
		$reply = $this->GetReply();
		$code = substr($reply, 0, 3);
		if($code != 250){
			$this->GetError(20);#服务器没有接受MAIL
			return false;
		}
		return true;
	}


	/*
	发送NOOP命令
	*/
	function Noop()
	{
		$this->PutCmd('Noop');
		$reply = $this->GetReply();
		$code = substr($reply, 0, 3);
		if($code != 250){
			$this->GetError(9);#服务器没有接受NOOP
			return false;
		}
		return true;
	}


	/*
	发送QUIT命令
	*/
	function Quit($close = true)
	{
		$this->PutCmd('quit');
		$reply = $this->GetReply();
		$code = substr($reply, 0, 3);
		if($code != 221){
			$this->GetError(10);#服务器没有接受QUIT
			return false;
		}
		if($close) return $this->close();
		return true;
	}

	/*
	发送RCPT命令
	*/
	function Rcpt($to)
	{
		$this->PutCmd('RCPT TO:<'.$to.'>');
		$reply = $this->GetReply();
		$code = substr($reply, 0, 3);
		if($code != 250 && $code != 251){
			$this->GetError(11);#服务器没有接受RCPT
			return false;
		}
		return true;
	}


	/*
	发送RESET命令
	*/
	function Reset()
	{
		$this->PutCmd('RSET');
		$reply = $this->GetReply();
		$code = substr($reply, 0, 3);
		if($code != 250){
			$this->GetError(12);#服务器没有接受RSET
			return false;
		}
		return true;
	}

	/*
	发送SEND命令
	*/
	function Send($from)
	{
		$this->PutCmd('SEND FROM:'.$from);
		$reply = $this->GetReply();
		$code = substr($reply, 0, 3);
		if($code != 250){
			$this->GetError(13);#服务器没有接受SEND
			return false;
		}
		return true;
	}


	/*
	发送SAML命令
	*/
	function Saml($from)
	{
		$this->PutCmd('SAML FROM:'.$from);
		$reply = $this->GetReply();
		$code = substr($reply, 0, 3);
		if($code){
			$this->GetError(14);#服务器没有接受SAML
			return false;
		}
		return true;
	}


	/*
	发送SOML命令
	*/
	function Soml($from)
	{
		$this->PutCmd('SOML FROM:'.$from);
		$reply = $this->GetReply();
		$code = substr($reply, 0, 3);
		if($code != 250){
			$this->GetError(15);#服务器没有接受SOML
			return false;
		}
		return true;
	}


	/*
	发送VRFY命令
	*/
	function Vrfy($name)
	{
		$this->PutCmd('VRFY '.$name);
		$reply = $this->GetReply();
		$code = substr($reply, 0, 3);
		if($code != 250 && $code != 251){
			$this->GetError(16);#服务器没有接受VRFY
			return false;
		}
		return $reply;
	}

	########################一些设置######################

	/*
	设置SMTP命令行的结束符
	*/
	function SetLn($ln)
	{
		$this->LN = $ln;
		return true;
	}


	/*
	设置是否进行显性报错
	*/
	function SetError($is)
	{
		if((int)$is == 1 || $is == true || strtolower($is) == 'on') $this->isError = 'on';
		else if((int)$is == 0 || $is == false || strtolower($is) == 'off') $this->isError = 'off';
		return $this->isError;
	}


	/*
	设置是否将工作状态置为调试
	*/
	function SetDebug($is)
	{
		if((int)$is == 1 || $is == true || strtolower($is) == 'on') $this->isDebug = 'on';
		else if((int)$is == 0 || $is == false || strtolower($is) == 'off') $this->isDebug = 'off';
		return $this->isDebug;
	}


	/*
	向SMTP服务器发送命令
	*/
	function PutCmd($cmd)
	{
		if(!$this->lk) $this->Connect();
		if(!$this->lk) return false;
		if($this->isDebug == 'on') echo $this->LN.'SMTP SEND:'.$this->LN.$cmd;
		fputs($this->lk, $cmd.$this->LN);
		return true;
	}


	/*
	取SMTP服务器回复信息
	*/
	function GetReply()
	{
		if(!$this->lk) return false;
		$data = "";
		while($str = @fgets($this->lk, 515)){
			$data .= $str;
			if(substr($str, 3, 1) == " ") break;
		}
		if($this->isDebug == 'on') echo $this->LN.'SMTP Receive:'.$this->LN.$data;
		return $data;
	}


	/*
	报错处理
	*/
	function GetError($errno = 0)
	{
		#错误号与错误信息
		$errorArr[1] = '缺少连接SMTP服务器的参数';
		$errorArr[2] = 'SMTP服务器没有接受身份验证机制';
		$errorArr[3] = 'SMTP服务器没有接受用户名';
		$errorArr[4] = 'SMTP服务器没有接受用户密码';
		$errorArr[5] = 'SMTP服务器没有接受EXPN指令';
		$errorArr[6] = 'SMTP服务器没有接受EHLO指令';
		$errorArr[7] = 'SMTP服务器没有接受HELO指令';
		$errorArr[8] = 'SMTP服务器没有接受HELP指令';
		$errorArr[9] = 'SMTP服务器没有接受NOOP指令';
		$errorArr[10] = 'SMTP服务器没有接受QUIT指令';
		$errorArr[11] = 'SMTP服务器没有接受RCPT指令';
		$errorArr[12] = 'SMTP服务器没有接受RSET指令';
		$errorArr[13] = 'SMTP服务器没有接受SEND指令';
		$errorArr[14] = 'SMTP服务器没有接受SAML指令';
		$errorArr[15] = 'SMTP服务器没有接受SOML指令';
		$errorArr[16] = 'SMTP服务器没有接受VRFY指令';
		$errorArr[17] = 'SMTP服务器没有接受DATA指令';
		$errorArr[18] = 'SMTP服务器没有接受DATA数据';
		$errorArr[19] = '缺少登陆SMTP服务器用户名或密码';
		$errorArr[20] = 'SMTP服务器没有接受MAIL指令';
		$errno = (int)$errno;
		if($errno) $this->errno = $errno;

		if(!empty($errorArr[$this->errno])){
			if($this->isError == 'on'){#打印并返回错误信息
				echo '<font color="red">ERROR:'.$errorArr[$this->errno].'</font>';
				return $errorArr[$this->errno];
			}else{#返回错误号和错误信息
				//echo '<!--SMTP ERRNO:'.$this->errno.'-->';
				return array($this->errno,$errorArr[$this->errno]);
			}
		}
		return false;
	}


}







